/*
 * @FileName: boot.c
 * @Author: GreyQiu qiushaogui@aikosolar.com
 * @Date: 2023-07-13 15:53:43
 * @LastEditors: GreyQiu qiushaogui@aikosolar.com
 * @LastEditTime: 2023-08-23 15:59:41
 * @Description: 
 * 本模块实现在芯片flash中模拟eeprom
 * 注意！！ 本模块对上层应用屏蔽了EEPROM在FLASH中的具体位置
 * 用户仅认为eeprom基地址从0开始以及大小即可，无需考FLASH中的实际位置
 * 
 * Copyright (c) 2023, All Rights Reserved. 
 */

#include "boot_flash.h"

/*
 * typedef enum
 * {
 *  APP_FLASH__BUSY = 1,
 *  FLASH_ERR_PG,
 *  FLASH_ERR_WRP,
 *  FLASH_EOP,
 *  FLASH_ERR_RDP2,
 *  FLASH_ERR_ADD,
 *  FLASH_TIMEOUT
} FLASH_STS;
 */

/**
 * @description eeprom状态与厂商提供的FLASH驱动中的FLASH状态映射表
 */
static const flash_sts_t sts_map[] = {
    APP_FLASH_NONE,
    APP_FLASH_BUSY,      // 操作忙
    APP_FLASH_ERR_PG,    // 编程错误
    APP_FLASH_ERR_WRP,   // 写保护
    APP_FLASH_SUCCESS,   // 操作成功
    APP_FLASH_ERR_RDP,   // 读保护
    APP_FLASH_ERR_ADD,   // 非法地址
    APP_FLASH_TIMEOUT    // 操作超时
};

/**
 * @description: 擦除eeprom一个page
 * @param {uint32_t} address page地址
 * @return 操作结果
 */
flash_sts_t app_flash_page_erase(uint32_t address)
{
    FLASH_STS fsts;
    // 判断地址是否属于eeprom
    if (address >= APP_SIZE) {
            return APP_FLASH_ERR_ADD; // 非法地址
    }
    FLASH_Unlock();
    fsts = FLASH_One_Page_Erase(address + APP_BASE_ADDR);
    FLASH_Lock();
    return sts_map[fsts];
}

/**
 * @description: 往eeprom指定地址写入指定长度的数据
 * @param {uint32_t} address 要写入的eeprom地址
 * @param {uint8_t *} pData 要写入的数据地址
 * @param {uint32_t} byteSize 要写入的字节数
 * @return 操作结果
 */
flash_sts_t app_flash_write(uint32_t address, uint8_t * pData, uint32_t byteSize)
{
    FLASH_STS fsts;

    // 判断目标地址是否属于eeprom 以及 要写入的数据大小是否合法
    if (address >= APP_SIZE || 
        (address + byteSize) > APP_SIZE) {
            return APP_FLASH_ERR_ADD; // 非法地址
    }
    // 判断是否跨页编程
    if (address / FLASH_PAGE_SIZE != (address + byteSize) / FLASH_PAGE_SIZE) {
        return APP_FLASH_ERR_ADD; // 非法地址
    }

    // FLASH_Unlock();
    // 按字编程
    // for (uint32_t i = 0; i < byteSize; i += FLASH_WORD_SIZE) {
    //     fsts = FLASH_Word_Program(address + EEPROM_BASE_ADDR, *((uint32_t *)pData));
    //     if (fsts != FLASH_EOP) { // 如果出错，提前退出循环
    //         break; // 编程错误
    //     }
    //     address += FLASH_WORD_SIZE;
    //     pData += FLASH_WORD_SIZE;
    // }

    uint32_t pDest = address + APP_BASE_ADDR;
    FLASH_Unlock();
    FLASH->CTRL |= FLASH_CTRL_SET_PG; // 启动FLASH编程模式
    // 按字编程
    for (uint32_t i = 0; i < byteSize; i += FLASH_WORD_SIZE) {
        *(__IO uint32_t*)pDest  = (*((uint32_t *)pData))++;
        pDest += FLASH_WORD_SIZE;
        pData += FLASH_WORD_SIZE;
    }

    fsts = FLASH_Last_Operation_Wait(PROGRAM_TIMEOUT);
    FLASH->CTRL &= FLASH_CTRL_RESET_PG; // 关闭FLASH编程模式
    FLASH_Lock();

    return sts_map[fsts];
}

/**
 * @description: 从eeprom指定地址读取指定长度的数据
 * @param {uint32_t} address 要读取的eeprom地址
 * @param {uint8_t *} pData 数据要存放的缓存地址
 * @param {uint32_t} byteSize 要读取的字节数
 * @return 操作结果
 */
flash_sts_t app_flash_read(uint32_t address, uint8_t * pData, uint32_t byteSize)
{
    // 判断目标地址是否属于eeprom 以及 要写入的数据大小是否合法
    if (address >= APP_SIZE || 
        (address + byteSize) > APP_SIZE) {
            return APP_FLASH_ERR_ADD; // 非法地址
    }

    const uint8_t * pDest = (const uint8_t *)(address + APP_BASE_ADDR);
    for (uint32_t i = 0; i < byteSize; i++) {
        *pData = *pDest;
        pData++;
        pDest++;
    }
    return APP_FLASH_SUCCESS;
}
